A high-level application control system for RISC OS. The system is given the name Impulse II because it contains many of the ideas used in the original version of Impulse.
Impulse II is a system of passing messages between tasks to command them to do things not normally possible using the defined Wimp messages. The messages contain simple textual commands so that they can be set up easily by the user - bringing him the ability to use his powerful application programs in new ways.
The Impulse II system will be used to implement mail-merging in Impression and hot-links between programs such as Equasor, Impression, ShowPage and others. It will also be used by small utilities such as !Measure and timed auto-save/auto-backup, etc. In the future ABIáII (our new 3D User-Interface Management System) will use Impulse messages to transfer information between applications and their user-interfaces. When ABI II is in use, our applications will become completely general-purpose servers which do nothing more than service requests to manipulate their objects.
The syntax of Impulse commands is defined to be future-proof. Although the commands defined at the moment are quite simple, they will be able to support object-oriented programming, resource protection by capabilities, distributed systems, etc... You will already see some of the terminology of object-oriented programming used in this document. True object-orientation is not yet defined in the Impulse system (in particular the concept of classes) but it will be formalised in the future.
A small RISC OS application will be written to allow the user to directly type in and send Impulse commands around the system. This will take the form of a scrolling text window in which commands are typed and replies are shown. It is intended that, one day, there will exist a program which adds a shell of looping keywords, conditional keywords and variables around the Impulse message commands so that Impulse scripts can be run.
We will make Impulse II public domain and we would like to see Impulse become established as the RISCáOS inter-application communication standard. Any comments or suggestions on the proposed ssystem are welcomed.
The syntax of the parameters is defined by the server.
ObjectPaths
ObjectPaths are paths from the current machine to the object on which the method is to be performed. If the first Identitifer in the ObjectPath matches the name of another machine Impulse will send the message to that machine. If the first Identifier in the ObjectPath does not match the name of another machine Impulse assumes that the path specifies an object on the local machine.
Some example ObjectPaths,
Impression.MergeLet This object path can have two meanings. It either specifies document "MergeLet" in application "Impression" on the local machine, or if the local machine is connected to another called "Impression", the commad will be sent to an application called "MergeLet" on that machine!
$.Paint5 Specifies application "Paint5" on the local machine.
Fred.Squirrel.Users If the local machine is connected to another named "Fred" then this path specifies document "Users" in application "Squirrel" on machine "Fred".
The $ symbol is used to specify the local machine. It is used to force Impulse to try to resolve the object path only locally to that machine. Obviously, if the machine is not connected to any others the $ symbol doesn't need to be used at all!
The ObjectPath is not always needed in command strings. When the command is being broadcast to all applications on a machine it can be ommitted. Also, the client and server tasks can agree a protocol which allows the client to refer to the object it wants to use by name once only and from then on use a faster, more compact object reference. (Such a protocol for use by all applications will be defined by us at a later date!)
Methods
The operations that servers can perform on behalf of the client are called methods. Servers give their methods textual names to be used in command strings. The Impulse module decodes commands for the server telling it which method is being called but it is the responsibility lfe server to extract parameters from commands. The commands are intended to be entered by ordinary users, not programmers, and so their design should be very simple. The method names should attempt to describe what they do.
Proposed mail merge commands
Merge
ClearMerge
Proposed general commands
Save
Print
Edit On | Off
Create
Destroy
Proposed Impulse commands
ImpulseDescribeMethods
ImpulseDescribeClasses
Possible database commands
GetField <Integer>
If <expression> Then ... Else ...
Some example command strings
:$.Impression.Letter23 DoMerge
:Impression.Letter23 Edit Off
:$.Squirrel.Customers If Field(1)="Mr" Then ="His" Else ="Her"
The possible replies to these command messages also take the form of strings but no machine name or method names are included - just return parameters.
The size of the Impulse messages are constrained by the size of the Acorn message block, ie. 256 bytes minus the size of the header, hence only limited parameters can be passed in the original command message. Longer parameters follow in further messages. The original command message and its reply must always be readable by humans, ie. it is always textual. The longer parameters can be passed in any form - they are not expected to be readable by humans.
Remote method calls
As described above the ObjectPaths allow for the message to be sent off the local machine to other machines connected to it. This is made possible by tasks called Link Servers which register themselves with Impulse in the normal way. Each one controls the hardware/software link between the local machine and one or more other machines.
For example, consider the Econet link server. It will register itself to Impulse with the name öEconetò, so any ObjectPath which starts ö:Econet.ò will be sent to the Econet link server. The message will be sent on the network to the machine specified by the unresolved part of the ObjectPath. When the message is received from the network by the link server on the receiving machine, it adjusts the message before sending it on that machine by calling Impulse_SendMessage.
Note that if a message has been transferred over one network to a machine which is connected to another the ObjectPath in the message could cause the message to be sent on the second network. Thus the machine connected to both networks automatically becomes a gateway between them for Impulse messages without any special ögatewayò software being used.
It is entirely up to the local Econet link server and those on the other machines to control the naming of machines/users and to make network communications efficient. The link servers would typically broadcast machine names and user names to each other at regular intervals so that each link server can send Impulse messages directly to the machine that they are intended for.
For more details on link servers see the section öImplementing Link Serversò.
Impulse II message format
Two message action codes are defined, Command and Reply. They are very similar in format and both Impulse II messages will, of course, have the normal Acorn message header.
The format of the command message is (with r1 pointing at the base of the message):
R1+16 #Message_ImpulseCommand
R1+20 Flags
R1+24 Offset to command string from message base (n)
R1+28 Offset to unresolved part of ObjectPath from first character of ObjectPath
R1+32 Estimated size of data in bytes
...
R1+n Command string
The format of the reply message is (with r1 pointing at the base lfe message):
R1+16 #Message_ImpulseReply
R1+20 Flags
R1+24 Offset to command string from message base (n)
R1+28 Reserved - must be zero
R1+32 Estimated size of data in bytes
...
R1+n Reply string
The Flags word and offset word are provided for future expansion lfe message format.
The bits in the flags word have the following meanings:
Bit Meaning when set
0 Close channel after message
31 Error (Only meaningful in reply messages)
All other flags will be set to zero but tasks should not assume that they are zero when receiving messages.
The Offset word allows other data to be included in the message. At the moment the command string will usually start at byte 32, directly after the Resolution word, but tasks should not assume this position!
The Resolution word points into the ObjectPath to show which part of it has yet to be resolved, if it exists. The name pointed to by the Resolution word represents the next place where the command will be sent. Once a command has been sent to a server task the Resolution word will point to the name of the object (if there is one) within the server to which the command should apply.
Impulse II message protocol
The message passing protocols take the form of request-reply pairs between client and server tasks. The client (the task sending request messages) will perform these operations:
Send command message; {Setting DataEstimate if wanting to send data}
if command.DataEstimate<>0 then
begin
while more data do
begin
Await RAMFetch;
Wimp_BlockTransfer;
Send RAMTransmit;
end; {while}
end; {if DataEstimate<>0}
if need reply then
begin
Await reply;
if reply.DataEstimate<>0 and want data then
repeat
Send RAMFetch;
Await RAMTransmit;
until no more to fetch;
end; {if need reply}
Note that the client does not actually wait for any replies unless it wants to and that one conversation can transfer large amounts of data in both directions.
The data transmission is carried out using protocols already defined by Acorn for in-memory transfer. The command messages need not be sent to specific tasks, they can be broadcast.
When sending commands it is important not to assume that replies will come back before other events occur. Although messages have a high priority in the Wimp and are returned in preference to all other types of event, the reply to an Impulse command may have to come back from a remote machine and the delay before it arrives could be considerable.
Tasks will not need to worry about most of this protocol! The Impulse II module will provide SWIs to send requests and deal with replies and the associated transfer of data.
This system allows programs issuing Impulse commands to be completely ignorant lfe content of the commands. If a task dealing with Impulse messages receives a reply or data transfer message that it does not want to process (maybe it has no data to send or does not wish to receive the incoming data) it simply ignores the message. This will cause an "undelivered" message to be returned to the sender and he will abort the operation without complaint.
Impulse II module
A RISC OS module will be provided to help applications use the Impulse II system. The module will decode incoming messages and send messages for the application. To allow the Impulse module to decode incoming Impulse messages the application will supply it with a table describing methods which it implements.
Note that the following descriptions of SWI calls describe the module as it stands at the time of writing and they may change before the module is released.
The SWI descriptions given below apply to Impulse module version 0.17.
Impulse_Initialise
(SWI &428C0)
Announces that a task is going to be an Impulse server or client.
On entry R0 = last Impulse version number known to task * 100
R1 = task handle of caller (as returned by Wimp_Initialise)
R2 = pointer to application name
R3 = -1
On exit R0 = current Impulse version number * 100
R1-R3 preserved
Interrupts Interrupts are enabled
Fast interrupts are enabled
Processor mode Processor is in SVC mode
Re-entrancy SWI is not re-entrant
Use This call registers the task as being an Impulse user - eiher a client or a server. It also ensures that the task's name has not already been registered by another task.
Impulse_Initialise should only be called once when the task starts up. It must be called after "Wimp_Initialise" has been called and before any other call to the Impulse module is made.
Obviously, a program cannot be an Impulse client or server without being a Wimp task or else it would not receive any Impulse messages.
Related SWIs None
Related vectors None
Impulse_Decode
(SWI &428C1)
Decodes Impulse messages into unique tokens.
On entry R0 = reason code (from Wimp_Poll)
R1 = pointer to block (from Wimp_Poll, data depends on reason code)
R5 = pointer to method structure
R6 = task handle of caller (as returned by Wimp_Inititialise)
On exit R0 = reason code
or &200 if Impulse command message received
or &201 if Impulse request message received
or &202 if Impulse reply message received
or &203 if Impulse RAMFetch received
or &204 if Impulse RAMTransmit received
or -1 if message was dealt with
R1 = pointer to block (data depends on reason code)
If R0 = &200,&201 (Impulse_Command or Impulse_Request) then
R5 = method token
R6 = ptr to parameter results after OS_ReadArgs or ptr to params or -1
R7 = pointer to object name string
If R0 = &202 (Impulse_Reply) then
R5 = reply tag or -1 if it's an unexpected reply
R6 = pointer to reply string or -1 if no reply was sent
If R0 = &203 (Impulse_SendData) then
R5 = reply tag
R7 = Max. size of data that should be sent
If R0 = &204 (Impulse_ReceiveData) then
R5 = reply tag
R6 = Size of data expected
R7 = Size of data that was sent
Interrupts Interrupts are enabled
Fast interrupts are enabled
Processor mode Processor is in SVC mode
Re-entrancy SWI is not re-entrant
Use This call traps incoming Impulse II messages and deals with them for the task. It must be called if a task is going to use Impulse for any reason! It directs incoming replies on to the appropriate routines and decodes the method name from the command string into a unique number. Only Poll_Wimp events 17, 18 and 19 are trapped, so the task can call Impulse_Decode only when one of those events has been received. This ensures that the other event handlers (the Null event handler in particular) are called as quickly as possible.
When an Impulse message is sent to the task, this SWI captures it and checks that the machine name is correct - if not the message is ignored. It then decodes the method name against those supplied in the method structure.
The method structure format is:
Structure format word (always 0 at the moment)
Followed by any number of the following records:
Method information flags (always 0)
Method token
Method name string, zero terminated
Parameter syntax string, zero terminated
Align to word
Terminated by:
Invalid flags word (-1)
Invalid method token (-1)
Null method name (""+CHR$0)
Null parameter syntax string (""+CHR$0)
Align to word
The method name strings must not contain ASCII character code 127 or any codes lower than 33. The matching of method names in the structure against method name in the messages is not case sensitive. In cases where one method name is the root of another, for example, "Get" and "GetField", the shortest one should appear last in the method structure.
The syntax strings are used by the Impulse module to call OS_ReadArgs to deal with the command parameters. See the description lf that SWI, pp. 597-600 of PRM volume II for more information.
The method token is the number passed back to the caller in R5 if the command message is succesfully parsed by Impulse.
If the event passed to Impulse_Decode is not an Impulse message then the register values from Wimp_Poll are returned unaltered. If the event was an Impulse message then the reason code is altered to be one lfe Impulse event reason codes &200 to &205.
To handle these new reason codes tasks should simply do something like this:
.PollLoop
mov r0,#0 ; Allow all events
add r1,r12,#PollBuff ; Point to data buffer
swi "Wimp_Poll" ; Wait for an event
cmp r0,#17 ; If it's a message
adrhs r5,MethodTable ; Then get method table addr
ldrhs r6,[r12,#TaskID] ; and get my task handle
swihs "Impulse_Decode" ; and give Impulse a look
bl PollEvent ; Go deal with event
bal PollLoop ; Go around again...
; This subroutine dispatches events to their handlers.
.PollEvent
cmp r0,#&200 ; If Reason code is valid
bhs ImpulseEvent
... ... ...
; Normal Wimp event handling here...
.ImpulseEvent
sub r0,r0,#&200
cmp r0,#&205-&200
addls pc,pc,r0,asl #2 ; Then jump to handler
movs pc,r14 ; >&205 (Invalid reason)
; The following events are generated by Impulse_Decode...
bal ImpulseMethod ; &200 (Impulse_Command)
bal ImpulseMethod ; &201 (Impulse_Request)
bal ImpulseReply ; &202 (Impulse_Reply)
bal ImpulseSend ; &203 (Impulse_SendData)
bal ImpulseReceive ; &204 (Impulse_ReceiveData)
movs pc,r14 ; &205 (Impulse_DefferedReply)
If the event passed to Impulse_Decode is a message which is handled by the module but does not contain a command (eg. RAMFetch, RAMTransmit or an undelivered message) then the event reason code returned will be -1. If the jump table is set up as described above this will jump through the invalid reason code handler.
Impulse_Command The returned block contains an Impulse Command message as described Impulse_Request above. When these reason codes are returned then R5 is either set to the appropriate token from the method structure or -1 if there was some sort of syntax error in the command.
R6 points to the parameter block output by calling OS_ReadArgs on the parameter string using the syntax string from the method structure. If no syntax string was supplied then R6 points to the parameter string itself. R6 is -1 if the call to OS_ReadArgs resulted in an error.
R7 points to the local object name within the server. This will be a null string if no local object name was supplied. If the command string includes the optional ObjectPath then Impulse will already have resolved any machine names and the name of the server by the time it is passed to the server via one lf these events. The remaining part of the ObjectPath must therefore refer to an object controlled by the server and it should make sure that the local object name is valid before performing the method. When R7 points to a null string the server should treat the method as applying to itself rather than an object within it.
Impulse_Reply When an Impulse reply is received it is passed on to the user via reason code &202 (Impulse_Reply). R5 holds the value of the reply tag which was passed to Impulse_SendMessage so that the task can identify which request the reply belongs to. If the reply was not originally sent by that task calling Impulse_SendMessage R5 contains -1.
Impulse_SendData The returned block contains a RAMFetch message as described in the PRMs although the program should not need to look at it.
This event is returned when a RAMFetch message is received as part of an Impulse conversation between client and server tasks. The program should respond to the event by sending a chunk of data back to the other task using SWI Impulse_TransmitData.
Impulse_ReceiveData The returned block contains a RAMTransmit message as described in the PRMs - but the program should not need to use it.
This event is returned when a RAMTransmit message is received as part of an Impulse conversation between client and server tasks. The program should respond by checking to see if its buffer was filled by the transmitted data (if r6=r7) and if so, it should call SWI Impulse_FetchData to request any remaining data from the other task.
Related SWIs None
Related vectors None
Impulse_SendMessage
(SWI &428C2)
Build and send an Impulse message on behalf of a client task.
R7 = estimated size of data to follow this message
On exit R0 is preserved
R1 = pointer to message block
R2 = task handle of destination (except for broadcasts)
R4 = MyRef of message sent if a reply is expected to returned in response
Interrupts Interrupts are enabled
Fast interrupts are enabled
Processor mode Processor is in SVC mode
Re-entrancy SWI is not re-entrant
Use This call builds and sends an Impulse command message. Its parameters are similar to Wimp_SendMessage except for some small differences. Only a limited range of reason codes are accepted in R0, all others will be faulted:
Code Meaning
&200 Impulse_Command (send command, no reply expected)
&205 Impulse_DefferedReply (send reply to last Impulse_DeferReply)
Needs: R0, R1, R5, R6
R1 does not point to a message block on entry, instead it points at the string which the SWI builds into a suitable message block. On exit, however, the pointer to the message block that was built is returned so that the caller can, if needed, inspect the header fields.
The reply tag in R5 is used to identify the reply when it is returned to the task via Impulse_Decode. The Impulse module stores the my_ref field of the message along with the tag and Impulse_Decode uses this information to detect incoming replies by matching their your_ref fields against the stored references. The values of tags and their use are entirely up to the caller.
R7 gives the estimated amount of data to follow the message. Setting R7 to zero means that the caller does not wish to send any data after this message. When R7 is non-zero the caller is telling whoever receives the message that it would like to transfer some data following this message. If the receiver is able to receive the data then Impulse_SendData events will be generated to transfer the data across. The caller may transfer more data than the amount given in the original estimate.
Codes &200 and &201
The destination task description in r2 and r3 is as described in the PRMs, it can specify the message to be sent to a particular task, broadcast to all tasks, sent to the owner of window or the owner of an icon on the icon bar. The reply tag is not checked to see if it has been used before! This allows you to give different messages the same reply tag.
Code &202 & &205
When calling Impulse_SendMessage with reason code &202 (Impulse_Reply) the reply is automatically sent to the task which sent the last command received. The your_ref field of the reply is automatically set up from the last command. Note that this means that you must send any reply to a command before calling Wimp_Poll again. If you need to call Wimp_Poll before you can reply to the last message then you must postpone the reply using Impulse_DeferReply.
When calling Impulse_SendMessage with reason code &205 (Impulse_DefferedReply) the reply is sent to the task whose message was being processed when Impulse_DeferReply was called.
Related SWIs None
Related vectors None
Impulse_TransmitData
(SWI &428C3)
Send an Impulse data message on behalf of a client or server task.
On entry R0 = pointer to base lf data block to send
R1 = pointer to top of data block to send
R6 = task handle
On exit R4 = myRef reference of the RAMTransmit message just sent
Interrupts Interrupts are enabled
Fast interrupts are enabled
Processor mode Processor is in SVC mode
Re-entrancy SWI is not re-entrant
Use This call is used to send data to another task - usually as part of the handling code for an Impulse_SendData event during data transfer to another task. The Impulse_SendData event signals that the task receiving data is ready for more data to be sent to it. The sending task should prepare its data for transmission and call Impulse_TransmitData to send it. Impulse_TransmitData will then call Wimp_TranferBlock, build the appropriate RAMTransmit message and send it back to the receiving task.
The header of the RAMTransmit message is built by looking at the last Impulse message received and so Impulse_TransmitData must be called before the next Wimp_Poll.
Note that, at the moment, the sender must ensure that the size of data he sends is less than or equal to the buffer size supplied in the fetch message.
Both this call and Impulse_FetchData, described below, leave the handling lfe data buffers for sending and receiving data to the calling task. This enables tasks to do clever things like transforming data as they send it or to increase the size of data buffers as data is received.
Related SWIs None
Related vectors None
Impulse_FetchData
(SWI &428C4)
Receive an Impulse data message on behalf of a client or server task and send any following messages to get more data.
On entry R0 = pointer to base of data buffer for more data
R1 = pointer to top of data buffer for more data
R6 = task handle
On exit R4 = myRef reference field of the message just sent
Interrupts Interrupts are enabled
Fast interrupts are enabled
Processor mode Processor is in SVC mode
Re-entrancy SWI is not re-entrant
Use This call sends a request for more data to the sending task - usually as part lfe code handling an Impulse_ReceiveData event. It builds and sends a RAMFetch message to the sending task to request more data to be placed in the specified buffer.
The header of the RAMFetch message is built by looking at the last Impulse message received and so Impulse_FetchData must be called before the next Wimp_Poll.
Related SWIs None
Related vectors None
Impulse_CloseDown
(SWI &428C5)
Tell Impulse that a client or server task is about to die.
On entry R0 = task handle returned by Wimp_Initialise
On exit ÿ
Interrupts Interrupts are enabled
Fast interrupts are enabled
Processor mode Processor is in SVC mode
Re-entrancy SWI is not re-entrant
Use This call should be made just before Wimp_CloseDown is called. It tells Impulse to remove all records lfe task from its tables and to tidy up any outstanding conversations with other tasks.
Related SWIs None
Related vectors None
Impulse_DeferReply
(SWI &428C6)
Postpone a reply message being sent.
On entry ÿ
On exit ÿ
Interrupts Interrupts are enabled
Fast interrupts are enabled
Processor mode Processor is in SVC mode
Re-entrancy SWI is not re-entrant
Use When a task does not respond to a request event by replying before the next call to Wimp_Poll a negative acknowledgement event is normally sent to the task which made the request. However, this is not always useful because the replying task may need to call Wimp_Poll several times before it can send the reply. In these circumstances Impulse_DeferReply can be used to prevent the negative acknowledgement until the task is ready to reply. When the task is ready to reply, it should call Impulse_SendMessage with reason code Impulse_DefferedReply.
Only one reply can be deffered at a time!
Related SWIs None
Related vectors None
Implementing Link Servers
There are complications involved in writing link servers due to the way that Acorn Wimp messages work. Firstly, negative acknowledgments are normally returned to the sender of a message if the receiver does not reply befoer he next calls Wimp_Poll. Secondly, the my_ref and your_ref fields are only unique on local machine - they will quite possibly be duplicated on other machines.
The link server will probably send its messages across the network in the background while the Wimp continues to multi-task and so it must return to Wimp_Poll before any reply is received to a message sent over the network. To avoid a negative acknowledgement event in the client task, the link server must defer the reply...
The link server must modify the my_ref and your_ref fields of messages received from other machines so that they are unique on the local machine. It must also be able to map the your_ref field of any reply message so that it will be correct when the reply is returned to the sedning machine.